package com.cloud.tool.config;

import com.cloud.tool.exception.ToolException;
import com.esotericsoftware.minlog.Log;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonSpringCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Classname RedisPlusConfig
 * @Date 2021/3/25 17:53
 * @Author WangZY
 * @Description Redis全新配置类
 */
@Configuration
public class RedisPlusConfig {
    @Autowired
    private ConfigurableEnvironment config;

    /**
     * 对 Redisson 的使用都是通过 RedissonClient 对象
     */
    @ConditionalOnProperty(name = "tool.redis-enable", havingValue = "true", matchIfMissing = true)
    @Bean(destroyMethod = "shutdown") // 服务停止后调用 shutdown 方法。
    public RedissonClient redisson() {
        String redisHost = config.getProperty("spring.redis.host");
        String redisPort = config.getProperty("spring.redis.port");
        String cluster = config.getProperty("spring.redis.cluster.nodes");
        String redisPassword = config.getProperty("spring.redis.password");
        Config config = new Config();
        //使用String序列化时会出现RBucket<Integer>转换异常
        //config.setCodec(new StringCodec());
        if (ObjectUtils.isEmpty(redisHost) && ObjectUtils.isEmpty(cluster)) {
            throw new ToolException("spring.redis.host及port或spring.redis.cluster" +
                    ".nodes必填，否则不可使用Redis");
        } else {
            if (StringUtils.hasText(cluster)) {
                // 集群模式
                String[] split = cluster.split(",");
                List<String> servers = new ArrayList<>();
                for (String s : split) {
                    servers.add("redis://" + s);
                }
                ClusterServersConfig clusterServers = config.useClusterServers();
                clusterServers.addNodeAddress(servers.toArray(new String[split.length]));
                if (StringUtils.hasText(redisPassword)) {
                    clusterServers.setPassword(redisPassword);
                }
                //修改命令超时时间为40s，默认3s
                clusterServers.setTimeout(40000);
                //修改连接超时时间为30s，默认10s
                clusterServers.setConnectTimeout(30000);
                clusterServers.setCheckSlotsCoverage(false);
            } else {
                // 单机模式
                SingleServerConfig singleServer = config.useSingleServer();
                singleServer.setAddress("redis://" + redisHost + ":" + redisPort);
                if (StringUtils.hasText(redisPassword)) {
                    singleServer.setPassword(redisPassword);
                }
                singleServer.setTimeout(40000);
                singleServer.setConnectTimeout(30000);
            }
        }
        RedissonClient redissonClient = null;
        try {
            redissonClient = Redisson.create(config);
        } catch (Exception e) {
            Log.error("初始化Redis失败", e);
        }
        return redissonClient;
    }

    @ConditionalOnProperty(name = "tool.redis-enable", havingValue = "true", matchIfMissing = true)
    @Bean
    public CacheManager cacheManager(RedissonClient redissonClient) {
        Map<String, CacheConfig> config = new HashMap<>();
        //开辟命名空间，过期设置为1小时，连接最大存活时间为30分钟
        config.put("springCache", new CacheConfig(60 * 60 * 1000, 30 * 60 * 1000));
        return new RedissonSpringCacheManager(redissonClient, config);
    }

//    /**
//     * 定义Jedis客户端，集群和单点同时存在时优先集群配置
//     */
//    @Bean
//    public JedisConnectionFactory redisConnectionFactory() {
//        String redisHost = config.getProperty("spring.redis.host");
//        String redisPort = config.getProperty("spring.redis.port");
//        String cluster = config.getProperty("spring.redis.cluster.nodes");
//        String redisPassword = config.getProperty("spring.redis.password");
//        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//        // 默认阻塞等待时间为无限长，源码DEFAULT_MAX_WAIT_MILLIS = -1L
//        // 最大连接数, 根据业务需要设置，不能超过实例规格规定的最大连接数。
//        jedisPoolConfig.setMaxTotal(100);
//        // 最大空闲连接数, 根据业务需要设置，不能超过实例规格规定的最大连接数。
//        jedisPoolConfig.setMaxIdle(60);
//        // 关闭 testOn[Borrow|Return]，防止产生额外的PING。
//        jedisPoolConfig.setTestOnBorrow(false);
//        jedisPoolConfig.setTestOnReturn(false);
//        JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().usePooling()
//                .poolConfig(jedisPoolConfig).build();
//        if (StringUtils.hasText(cluster)) {
//            // 集群模式
//            String[] split = cluster.split(",");
//            RedisClusterConfiguration clusterServers = new RedisClusterConfiguration(Arrays.asList(split));
//            if (StringUtils.hasText(redisPassword)) {
//                clusterServers.setPassword(redisPassword);
//            }
//            return new JedisConnectionFactory(clusterServers, jedisClientConfiguration);
//        } else if (StringUtils.hasText(redisHost) && StringUtils.hasText(redisPort)) {
//            // 单机模式
//            RedisStandaloneConfiguration singleServer = new RedisStandaloneConfiguration(redisHost, Integer
//            .parseInt(redisPort));
//            if (StringUtils.hasText(redisPassword)) {
//                singleServer.setPassword(redisPassword);
//            }
//            return new JedisConnectionFactory(singleServer, jedisClientConfiguration);
//        } else {
//            throw new ToolException("spring.redis.host及port或spring.redis.cluster" +
//                    ".nodes必填，否则不可使用RedisTool以及Redisson");
//        }
//    }


    /**
     * @Author WangZY
     * @Date 2021/3/25 17:55
     * @Description 如果配置了KeyGenerator ，在进行缓存的时候如果不指定key的话，最后会把生成的key缓存起来，
     * 如果同时配置了KeyGenerator和key则优先使用key。
     **/
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder key = new StringBuilder();
            key.append(target.getClass().getSimpleName()).append("#").append(method.getName()).append("(");
            for (Object args : params) {
                key.append(args).append(",");
            }
            key.deleteCharAt(key.length() - 1);
            key.append(")");
            return key.toString();
        };
    }

//    /**
//     * 配置Spring-Cache内部使用Redis，配置序列化和过期时间
//     * redis容易断连就别用，默认用org.springframework.cache.concurrent.ConcurrentMapCacheManager
//     */
//    @Bean
//    @ConditionalOnBean(JedisConnectionFactory.class)
//    @Deprecated
//    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
//        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer
//                = new Jackson2JsonRedisSerializer<>(Object.class);
//        ObjectMapper om = new ObjectMapper();
//        // 防止在序列化的过程中丢失对象的属性
//        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        // 开启实体类和json的类型转换，该处兼容老版本依赖，不得修改
//        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        jackson2JsonRedisSerializer.setObjectMapper(om);
//        // 配置序列化（解决乱码的问题）
//        RedisCacheConfiguration config = RedisCacheConfiguration.
//                defaultCacheConfig()
//                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
//                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer
//                (jackson2JsonRedisSerializer))
//                .disableCachingNullValues()// 不缓存空值
//                .entryTtl(Duration.ofMinutes(30));//30分钟不过期
//        return RedisCacheManager
//                .builder(connectionFactory)
//                .cacheDefaults(config)
//                .build();
//    }

//    /**
//     * @Author WangZY
//     * @Date 2021/7/2 11:50
//     * @Description springboot 2.2以下版本用，配置redis序列化
//     **/
//    @Bean
//    @Deprecated
//    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//        RedisTemplate<String, Object> template = new RedisTemplate<>();
//        template.setConnectionFactory(factory);
//        Jackson2JsonRedisSerializer json = new Jackson2JsonRedisSerializer(Object.class);
//        ObjectMapper mapper = new ObjectMapper();
//        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        json.setObjectMapper(mapper);
//        //注意编码类型
//        template.setKeySerializer(new StringRedisSerializer());
//        template.setValueSerializer(json);
//        template.setHashKeySerializer(new StringRedisSerializer());
//        template.setHashValueSerializer(json);
//        template.afterPropertiesSet();
//        return template;
//    }
}